Ray Tracing管线与Rasterization管线的差异
两种管线SSS实现的主要差异在于ScreenShadowMaskTexture
的渲染方式不同,在Ray Tracing管线中,使用RayTracingShadowMaskTexture
的内容写入到ScreenShadowMaskTexture
中,具体语句在LightRendering.cpp 中的FDeferredShadingSceneRenderer::RenderLights()
中:
GraphBuilder.QueueTextureExtraction(RayTracingShadowMaskTexture, &ScreenShadowMaskTexture); |
追踪RayTracingShadowMaskTexture
,发现其在RenderRayTracingShadows()
中渲染。在RayTracingShadows.cpp中找到FDeferredShadingSceneRenderer::RenderRayTracingShadows()
,可以在其中找到使用的shader类为FOcclusionRGS
,进一步找到绑定的shader文件为RayTracingOcclusionRGS.usf。且RWOcclusionMaskUAV
为最终传出的RayTracingShadowMaskTexture
。
IMPLEMENT_GLOBAL_SHADER(FOcclusionRGS, "/Engine/Private/RayTracing/RayTracingOcclusionRGS.usf", "OcclusionRGS", SF_RayGen); |
在RayTracingOcclusionRGS.usf中,RWOcclusionMaskUAV
的写入分为3种分支,其中不做降噪的分支如下,关于降噪分支会在后文提及:
const float ShadowFadeFraction = 1; |
而在UE4.25版本中,TransmissionDistance
直接被写为Visibility
,而Visibility
则是阴影项Shadow
的计算结果。这说明光追管线中没有计算SSS着色模型的次表面阴影项,而是暂时使用阴影项来代替,这直接导致了次表面散射效果的缺失。
Out.TransmissionDistance = (LocalSamplesPerPixel > 0) ? Out.Visibility / LocalSamplesPerPixel : Out.Visibility; |
Ray Tracing管线的修复
对于基于厚度的SSS算法,Ray Tracing管线相对于Rasterization管线的优势在于可以更加精确的计算出物体上一个像素被自身遮挡的厚度以及光线到达物体表面的辐射量,我们将其分别定义为Thickness和Transmittance,于是我们就着手计算。
// The thickness of itself |
画一张简单的示意图,以便覆盖所有可能的情况:
可以发现,我们需要拿到的数据有StartingPointID
、ClosestHitID
、Opacity
、HitT
等。
扩展Payload
UE4中定义了几种Payload,但是其中其实用到的只有FMaterialClosestHitPayload
和FPackedMaterialClosestHitPayload
,两者均继承自最基本的FMinimalPayload
,后者是前者的打包压缩版,目的是节约Payload的体积,增强光追的性能。FMaterialClosestHitPayload
的主要内容如下:
// Unpacked Packed |
其中并没有InstanceID
,于是我们需要扩展这个payload,如下:
|
同时也需要扩展FPackedMaterialClosestHitPayload
以及打包与解包的函数。
当这些做完之后可能会发现,编译后UE4会出现不明原因Crash,而且再也打不开了,这是为什么呢?
原因在于UE4其实在C++中指定了这个Payload的最大Size,当超过这个Size时,编译Shader,UE4就会直接Crash。
此时在Engine/Source/Runtime/Renderer/Private/RayTracing文件夹下的RayTracingAmbientOcclusion.cpp、RayTracingMaterialHitShaders.cpp、RayTracingShadows.cpp、RaytracingSkylight.cpp这四个文件中将// sizeof(FPackedMaterialClosestHitPayload)
注释处的Initializer.MaxPayloadSizeInBytes
修改为所需的新大小,即可解决这一问题。
可以发现的很重要的一点是,UE4为不同需求使用了不同的RayGenerationShader
(RayTracingDebugMainRGS
/OcclusionRGS
/RayTracingPrimaryRaysRGS
),但是对于不同材质其实都使用的是同一个ClosestHitShader
,即MaterialCHS
,同时也都使用同一个Payload,即FPackedMaterialClosestHitPayload
。
在UE4 Ray Tracing Debug View中显示InstanceID
为了验证UE4中每个物体的InstanceID
不同,同时方便调试,我们先将InstanceID
加入Ray Tracing Debug View。
首先在RayTracingDebug.usf添加一个case
,根据InstanceID
的值显示颜色:
case RAY_TRACING_DEBUG_VIZ_INSTANCE_ID: |
同时在Shaders/Shared/RaytracingDebugDefinitions.h中需要定义RAY_TRACING_DEBUG_VIZ_INSTANCE_ID
引擎中需要修改的文件有:
Runtime/Renderer/Private/RayTracing/RayTracingDebug.cpp,Editor/UnrealEd/Private/RayTracingDebugVisualizationMenuCommands.cpp
此时重新编译UE4,就可以从Ray Tracing Debug View中选择InstanceID,显示效果如下: